import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { Menus } from "./menu.entity";
import { LogsService } from "../logs/logs.service";
import { LogLevel } from "../logs/logs.entity";
import { CreateMenuDto } from "./dto/create-menu.dto";
import { UpdateMenuDto } from "./dto/update-menu.dto";
import { QueryMenuDto } from "./dto/query-menu.dto";
import type { PaginatedResponse } from "../../types/api-response.interface";
import { createPaginatedSuccessResponse } from "../utils/response";

/**
 * 菜单服务 - 处理菜单相关的业务逻辑
 */
@Injectable()
export class MenusService {
  constructor(
    @InjectRepository(Menus)
    private menuRepository: Repository<Menus>,
    private readonly logsService: LogsService,
  ) {}

  /**
   * 查询所有菜单（不分页）
   */
  async findAll(): Promise<Menus[]> {
    try {
      const menus = await this.menuRepository.find({
        relations: ["roles"],
        order: { order: "ASC", id: "ASC" },
      });

      // 记录操作日志
      await this.logsService.createSystemLog(
        `查询所有菜单（不分页），共找到 ${menus.length} 个菜单`,
        LogLevel.INFO,
        "MenusService.findAll",
        { total: menus.length },
      );

      return menus;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.findAll", undefined, {});
      throw new Error(`查询所有菜单失败: ${error.message}`);
    }
  }

  /**
   * 分页查询菜单列表，支持多条件搜索
   * @param queryDto 查询参数
   */
  async getPaginatedMenus(queryDto: QueryMenuDto = {}): Promise<PaginatedResponse<Menus>> {
    try {
      const { page = 1, pageSize = 10, name, path, acl } = queryDto;
      const limit = pageSize;

      // 构建查询
      const queryBuilder = this.menuRepository.createQueryBuilder("menu").leftJoinAndSelect("menu.roles", "roles");

      // 添加搜索条件
      if (name && name.trim()) {
        queryBuilder.andWhere("menu.name LIKE :name", { name: `%${name.trim()}%` });
      }

      if (path && path.trim()) {
        queryBuilder.andWhere("menu.path LIKE :path", { path: `%${path.trim()}%` });
      }

      if (acl && acl.trim()) {
        queryBuilder.andWhere("menu.acl LIKE :acl", { acl: `%${acl.trim()}%` });
      }

      // 计算总数
      const total = await queryBuilder.getCount();

      // 添加分页
      const skip = (page - 1) * limit;
      queryBuilder.skip(skip).take(limit);

      // 添加排序
      queryBuilder.orderBy("menu.order", "ASC").addOrderBy("menu.id", "ASC");

      // 执行查询
      const menus = await queryBuilder.getMany();

      // 计算总页数
      const totalPages = Math.ceil(total / limit);

      // 记录操作日志
      await this.logsService.createSystemLog(
        `分页查询菜单列表，页码: ${page}，每页: ${pageSize}，搜索条件: ${JSON.stringify({ name, path, acl })}，共找到 ${total} 个菜单`,
        LogLevel.INFO,
        "MenusService.getPaginatedMenus",
        {
          page,
          pageSize,
          searchConditions: { name, path, acl },
          total,
          totalPages,
        },
      );

      return createPaginatedSuccessResponse(menus, total, page, pageSize, "查询成功");
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.getPaginatedMenus", undefined, {
        queryParams: queryDto,
      });
      throw new Error(`查询菜单列表失败: ${error.message}`);
    }
  }

  /**
   * 根据ID查找菜单
   * @param id 菜单ID
   */
  async findOne(id: number): Promise<Menus> {
    try {
      if (!id || id <= 0) {
        throw new Error("菜单ID必须是正整数");
      }

      const menu = await this.menuRepository.findOne({
        where: { id },
        relations: ["roles"],
      });

      if (!menu) {
        throw new Error(`菜单 ID ${id} 不存在`);
      }

      // 记录操作日志
      await this.logsService.createSystemLog(
        `根据ID查找菜单: ${id}，菜单名: ${menu.name}`,
        LogLevel.INFO,
        "MenusService.findOne",
        { menuId: id },
      );

      return menu;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.findOne", undefined, { menuId: id });

      if (error.message.includes("菜单ID必须是正整数") || error.message.includes("不存在")) {
        throw error;
      }
      throw new Error(`查找菜单失败: ${error.message}`);
    }
  }

  /**
   * 创建新菜单
   * @param createMenuDto 菜单创建信息
   */
  async create(createMenuDto: CreateMenuDto): Promise<Menus> {
    try {
      // 检查菜单名称是否已存在
      const existingMenuByName = await this.menuRepository.findOne({
        where: { name: createMenuDto.name },
      });

      if (existingMenuByName) {
        throw new Error(`菜单名称 "${createMenuDto.name}" 已存在`);
      }

      // 检查菜单路径是否已存在
      const existingMenuByPath = await this.menuRepository.findOne({
        where: { path: createMenuDto.path },
      });

      if (existingMenuByPath) {
        throw new Error(`菜单路径 "${createMenuDto.path}" 已存在`);
      }

      // 使用 Repository 的 create 方法创建实体实例
      const menuEntity = this.menuRepository.create(createMenuDto);
      const newMenu = await this.menuRepository.save(menuEntity);

      // 记录操作日志
      await this.logsService.createSystemLog(
        `创建新菜单成功: ${newMenu.name} (ID: ${newMenu.id})，路径: ${newMenu.path}`,
        LogLevel.INFO,
        "MenusService.create",
        {
          newMenuId: newMenu.id,
          menuName: newMenu.name,
          menuPath: newMenu.path,
        },
      );

      return newMenu;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.create", undefined, {
        menuData: createMenuDto,
      });

      if (error.message.includes("已存在")) {
        throw error;
      }
      // 处理数据库唯一约束错误
      if (error.code === "ER_DUP_ENTRY") {
        throw new Error(`菜单信息重复，无法创建`);
      }
      throw new Error(`创建菜单失败: ${error.message}`);
    }
  }

  /**
   * 更新菜单信息
   * @param id 菜单ID
   * @param updateMenuDto 更新的菜单信息
   */
  async update(id: number, updateMenuDto: UpdateMenuDto) {
    try {
      if (!id || id <= 0) {
        throw new Error("菜单ID必须是正整数");
      }

      // 检查菜单是否存在
      const existingMenu = await this.menuRepository.findOne({
        where: { id },
      });

      if (!existingMenu) {
        throw new Error(`菜单 ID ${id} 不存在`);
      }

      // 如果更新菜单名称，检查是否重复
      if (updateMenuDto.name && updateMenuDto.name !== existingMenu.name) {
        const duplicateMenuByName = await this.menuRepository.findOne({
          where: { name: updateMenuDto.name },
        });

        if (duplicateMenuByName) {
          throw new Error(`菜单名称 "${updateMenuDto.name}" 已存在`);
        }
      }

      // 如果更新菜单路径，检查是否重复
      if (updateMenuDto.path && updateMenuDto.path !== existingMenu.path) {
        const duplicateMenuByPath = await this.menuRepository.findOne({
          where: { path: updateMenuDto.path },
        });

        if (duplicateMenuByPath) {
          throw new Error(`菜单路径 "${updateMenuDto.path}" 已存在`);
        }
      }

      const result = await this.menuRepository.update(id, updateMenuDto);

      if (result.affected === 0) {
        throw new Error(`更新菜单失败，没有记录被修改`);
      }

      // 记录操作日志
      await this.logsService.createSystemLog(
        `更新菜单信息成功: ${existingMenu.name} (ID: ${id})`,
        LogLevel.INFO,
        "MenusService.update",
        {
          menuId: id,
          menuName: existingMenu.name,
          updateFields: Object.keys(updateMenuDto),
        },
      );

      return result;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.update", undefined, {
        menuId: id,
        updateData: updateMenuDto,
      });

      if (
        error.message.includes("菜单ID必须是正整数") ||
        error.message.includes("不存在") ||
        error.message.includes("已存在") ||
        error.message.includes("更新菜单失败")
      ) {
        throw error;
      }
      // 处理数据库唯一约束错误
      if (error.code === "ER_DUP_ENTRY") {
        throw new Error(`菜单信息已存在，无法更新`);
      }
      throw new Error(`更新菜单失败: ${error.message}`);
    }
  }

  /**
   * 删除菜单
   * @param id 菜单ID
   */
  async remove(id: number) {
    try {
      // 参数验证
      if (!id || id <= 0) {
        throw new Error("菜单ID必须是正整数");
      }

      // 先获取菜单信息用于日志记录
      const existingMenu = await this.menuRepository.findOne({
        where: { id },
        relations: ["roles"],
      });

      if (!existingMenu) {
        throw new Error(`菜单 ID ${id} 不存在`);
      }

      const result = await this.menuRepository.delete(id);

      // 记录操作日志
      await this.logsService.createSystemLog(
        `删除菜单成功: ${existingMenu.name} (ID: ${id})，路径: ${existingMenu.path}`,
        LogLevel.WARN,
        "MenusService.remove",
        {
          deletedMenuId: id,
          deletedMenuName: existingMenu.name,
          deletedMenuPath: existingMenu.path,
        },
      );

      return result;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.remove", undefined, { menuId: id });

      // 处理自定义错误
      if (error.message.includes("菜单ID必须是正整数") || error.message.includes("不存在")) {
        throw error;
      }

      // 捕获外键约束错误并转换为友好消息
      if (error.code === "ER_ROW_IS_REFERENCED_2") {
        throw new Error(`无法删除菜单，存在角色关联数据。请先解除角色菜单关联，或联系管理员处理。`);
      }

      // 处理其他数据库错误
      if (error.code) {
        throw new Error(`删除菜单失败，数据库错误: ${error.message}`);
      }

      throw new Error(`删除菜单失败: ${error.message}`);
    }
  }

  /**
   * 根据访问控制权限查询菜单
   * @param acl 访问控制权限
   */
  async findByAcl(acl: string): Promise<Menus[]> {
    try {
      if (!acl || !acl.trim()) {
        throw new Error("访问控制权限不能为空");
      }

      const menus = await this.menuRepository.find({
        where: { acl: acl.trim() },
        relations: ["roles"],
        order: { order: "ASC", id: "ASC" },
      });

      // 记录操作日志
      await this.logsService.createSystemLog(
        `根据访问控制权限查询菜单: ${acl}，共找到 ${menus.length} 个菜单`,
        LogLevel.INFO,
        "MenusService.findByAcl",
        { acl, total: menus.length },
      );

      return menus;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.findByAcl", undefined, { acl });

      if (error.message.includes("不能为空")) {
        throw error;
      }
      throw new Error(`根据访问控制权限查询菜单失败: ${error.message}`);
    }
  }

  /**
   * 根据路径查找菜单
   * @param path 菜单路径
   */
  async findByPath(path: string): Promise<Menus | null> {
    try {
      if (!path || !path.trim()) {
        throw new Error("菜单路径不能为空");
      }

      const menu = await this.menuRepository.findOne({
        where: { path: path.trim() },
        relations: ["roles"],
      });

      // 记录操作日志
      await this.logsService.createSystemLog(
        `根据路径查找菜单: ${path}，${menu ? "找到菜单: " + menu.name : "未找到菜单"}`,
        LogLevel.INFO,
        "MenusService.findByPath",
        { path, found: !!menu },
      );

      return menu;
    } catch (error) {
      // 记录错误日志
      await this.logsService.createErrorLog(error, "MenusService.findByPath", undefined, { path });

      if (error.message.includes("不能为空")) {
        throw error;
      }
      throw new Error(`根据路径查找菜单失败: ${error.message}`);
    }
  }
}
